/*
 * Timer.c
 *
 *  Created on: 18-mrt-2012
 *      Author: Bart
 */

#include "Timer.h"

/************************** Variable Definitions *****************************/
/*
 * Volatile?
 * Indien de hardware deze variabalen kan veranderen.
 */
volatile Xuint32 timer0_overflow_count = 0;
volatile Xuint32 timer0_millis = 0;
static unsigned char timer0_fract = 0;

XTmrCtr TimerCounterInst;
XIntc InterruptController;

volatile int TimerExpired;

/************************** Function Prototypes ******************************/
//TODO best een algemene setupInterrupt van maken. -> In aparte C file
int setupIntrSystem(XIntc* IntcInstancePtr,
					XTmrCtr* TmrCtrInstancePtr,
					Xuint16 DeviceId,
					Xuint16 IntrId,
					Xuint8 TmrCtrNumber);

void TimerCounterHandler(void *CallBackRef, u8 TmrCtrNumber);

void TmrCtrDisableIntr(XIntc* IntcInstancePtr, u16 IntrId);



/*****************************************************************************/
/*
 *	Functions
 *
 *****************************************************************************/
int initialize(void){
	int Status;

	/*
	 * Initialize the timer counter so that it's ready to use.
	 */
	Status = XTmrCtr_Initialize(&TimerCounterInst, TMRCTR_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		if(Status != XST_DEVICE_IS_STARTED){
			return XST_FAILURE;
		}
	}

	/*
	 * Perform a self-test to ensure that the hardware was built correctly.
	 */
	Status = XTmrCtr_SelfTest(&TimerCounterInst, TMRCTR_DEVICE_ID);
	if (Status != XST_SUCCESS){
		return XST_FAILURE;
	}

	/*
	 *  Connect the timer counter to the interrupt subsystem such that interrupts can occur.
	 */
	Status = setupIntrSystem(&InterruptController,
							&TimerCounterInst,
							TMRCTR_DEVICE_ID,
							TMRCTR_INTERRUPT_ID,
							TIMER_CNTR_0);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Set interrupthandler
	 */
	XTmrCtr_SetHandler(&TimerCounterInst, TimerCounterHandler, &TimerCounterInst);

	/*
	 * Enable the interrupt of the timer counter so interrupts will occur
	 * and use auto reload mode such that the timer counter will reload
	 * itself automatically and continue repeatedly, without this option
	 * it would expire once only
	 */
	XTmrCtr_SetOptions(&TimerCounterInst, TIMER_CNTR_0,
				XTC_INT_MODE_OPTION | XTC_AUTO_RELOAD_OPTION | XTC_DOWN_COUNT_OPTION);

	/*
	 * Set a reset value for the timer counter such that it will expire
	 * eariler than letting it roll over from 0, the reset value is loaded
	 * into the timer counter when it is started
	 */
	XTmrCtr_SetResetValue(&TimerCounterInst, TIMER_CNTR_0, (int)RESET_VALUE);

	/*
	 * Start the timer counter such that it's incrementing by default,
	 * then wait for it to timeout a number of times
	 */
	XTmrCtr_Start(&TimerCounterInst, TIMER_CNTR_0);

	return XST_SUCCESS;
}

int setupIntrSystem(XIntc* IntcInstancePtr,
					XTmrCtr* TmrCtrInstancePtr,
					Xuint16 DeviceId,
					Xuint16 IntrId,
					Xuint8 TmrCtrNumber){
	int Status;

	/*
	 * Initialize the interrupt controller driver so that
	 * it's ready to use, specify the device ID that is generated in
	 * xparameters.h
	 */
	Status = XIntc_Initialize(IntcInstancePtr, INTC_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Connect a device driver handler that will be called when an interrupt
	 * for the device occurs, the device driver handler performs the specific
	 * interrupt processing for the device
	 */
	Status = XIntc_Connect(IntcInstancePtr, IntrId,
				(XInterruptHandler)XTmrCtr_InterruptHandler,
				(void *)TmrCtrInstancePtr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Start the interrupt controller such that interrupts are enabled for
	 * all devices that cause interrupts, specific real mode so that
	 * the timer counter can cause interrupts thru the interrupt controller.
	 */
	Status = XIntc_Start(IntcInstancePtr, XIN_REAL_MODE);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Enable the interrupt for the timer counter
	 */
	XIntc_Enable(IntcInstancePtr, IntrId);

	/*
	 * Initialize the exception table.
	 */
	Xil_ExceptionInit();

	/*
	 * Register the interrupt controller handler with the exception table.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
					(Xil_ExceptionHandler)
					XIntc_InterruptHandler,
					IntcInstancePtr);

	/*
	 * Enable non-critical exceptions.
	 */
	Xil_ExceptionEnable();

	return XST_SUCCESS;
}

TimerCounterHandler(void *CallBackRef, Xuint8 TmrCtrNumber)
{
	XTmrCtr *InstancePtr = (XTmrCtr *)CallBackRef;

	//TODO testen
	if (XTmrCtr_IsExpired(InstancePtr, TmrCtrNumber)){
		//Increment millis, overflow count

		timing0_millis += TIMING_INTERVAL;	// Check if TIMING_INTERVAL is in ms.
		timing0_overflow_count ++;

	}
}

Xuint32 millis(){
	Xuint32 m;

	//disable interrupt, otherwise we will get inconsistent values for millis.
	XIntc_Disable($InterruptController, INTC_DEVICE_ID);
	m = timing0_millis;
	XIntc_Enable($InterruptController, INTC_DEVICE_ID);
	return m;
}

Xuint32 micros(){
	Xuint32 m;
	Xuint32 micros;

	micros = timer0_overflow_count * TIMING_INTERVAL * 1000;

	Xuint32 timerValue = XTmrCtr_GetValue($TimerCounterInst, TMRCTR_DEVICE_ID);
	m = (RESET_VALUE - timerValue) * TIMING_INTERVAL * 1000 / RESET_VALUE;

	micros = micros + m;
	return micros;
}
